/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License"
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <gtest/gtest.h>

#include "plugins/ets/tests/mock/calling_methods_test_helper.h"

// NOLINTBEGIN(cppcoreguidelines-pro-type-vararg, readability-magic-numbers)

namespace panda::ets::test {

static const char *TEST_BIN_FILE_NAME = "CallingMethodsTest.abc";

class CallingMethodsTestGeneral : public CallingMethodsTestBase {
public:
    CallingMethodsTestGeneral() : CallingMethodsTestBase(TEST_BIN_FILE_NAME) {}
};

class MethodsTest : public CallingMethodsTestGeneral {};
class MethodsTestDeath : public CallingMethodsTestGeneral {};

TEST_F(MethodsTestDeath, CallMethodsTestGeneralDeath3)
{
    testing::FLAGS_gtest_death_test_style = "threadsafe";

    {
        // Call<type>Method part
        EXPECT_DEATH(env_->CallStaticVoidMethod(nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticObjectMethod(nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticBooleanMethod(nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticByteMethod(nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticCharMethod(nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticShortMethod(nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticIntMethod(nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticLongMethod(nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticFloatMethod(nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticDoubleMethod(nullptr, nullptr), "");

        // Call<type>MethodArray part
        EXPECT_DEATH(env_->CallStaticVoidMethodArray(nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticObjectMethodArray(nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticBooleanMethodArray(nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticByteMethodArray(nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticCharMethodArray(nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticShortMethodArray(nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticIntMethodArray(nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticLongMethodArray(nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticFloatMethodArray(nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticDoubleMethodArray(nullptr, nullptr, nullptr), "");

        // Call<type>MethodList part
        EXPECT_DEATH(CallStaticVoidMethodListHelper(env_, nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticObjectMethodListHelper(env_, nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticBooleanMethodListHelper(env_, nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticByteMethodListHelper(env_, nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticCharMethodListHelper(env_, nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticShortMethodListHelper(env_, nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticIntMethodListHelper(env_, nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticLongMethodListHelper(env_, nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticFloatMethodListHelper(env_, nullptr, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticDoubleMethodListHelper(env_, nullptr, nullptr, nullptr), "");
    }

    {
        ets_class cls = env_->FindClass("F");
        ASSERT_NE(cls, nullptr);

        // Call<type>Method part
        EXPECT_DEATH(env_->CallStaticVoidMethod(cls, nullptr), "");
        EXPECT_DEATH(env_->CallStaticObjectMethod(cls, nullptr), "");
        EXPECT_DEATH(env_->CallStaticBooleanMethod(cls, nullptr), "");
        EXPECT_DEATH(env_->CallStaticByteMethod(cls, nullptr), "");
        EXPECT_DEATH(env_->CallStaticCharMethod(cls, nullptr), "");
        EXPECT_DEATH(env_->CallStaticShortMethod(cls, nullptr), "");
        EXPECT_DEATH(env_->CallStaticIntMethod(cls, nullptr), "");
        EXPECT_DEATH(env_->CallStaticLongMethod(cls, nullptr), "");
        EXPECT_DEATH(env_->CallStaticFloatMethod(cls, nullptr), "");
        EXPECT_DEATH(env_->CallStaticDoubleMethod(cls, nullptr), "");

        // Call<type>MethodArray part
        EXPECT_DEATH(env_->CallStaticVoidMethodArray(cls, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticObjectMethodArray(cls, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticBooleanMethodArray(cls, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticByteMethodArray(cls, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticCharMethodArray(cls, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticShortMethodArray(cls, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticIntMethodArray(cls, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticLongMethodArray(cls, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticFloatMethodArray(cls, nullptr, nullptr), "");
        EXPECT_DEATH(env_->CallStaticDoubleMethodArray(cls, nullptr, nullptr), "");

        // Call<type>MethodList part
        EXPECT_DEATH(CallStaticVoidMethodListHelper(env_, cls, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticObjectMethodListHelper(env_, cls, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticBooleanMethodListHelper(env_, cls, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticByteMethodListHelper(env_, cls, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticCharMethodListHelper(env_, cls, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticShortMethodListHelper(env_, cls, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticIntMethodListHelper(env_, cls, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticLongMethodListHelper(env_, cls, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticFloatMethodListHelper(env_, cls, nullptr, nullptr), "");
        EXPECT_DEATH(CallStaticDoubleMethodListHelper(env_, cls, nullptr, nullptr), "");
    }
}
TEST_F(MethodsTest, CallMethodsTestGeneral3)
{
    ets_class cls = env_->FindClass("F");
    ASSERT_NE(cls, nullptr);

    ets_method void_id = env_->GetStaticp_method(cls, "void_method", "I:Lstd/core/void;");
    ASSERT_NE(void_id, nullptr);
    ets_method object_id = env_->GetStaticp_method(cls, "object_method", ":LA;");
    ASSERT_NE(object_id, nullptr);
    ets_method boolean_id = env_->GetStaticp_method(cls, "boolean_method", "Z:Z");
    ASSERT_NE(boolean_id, nullptr);
    ets_method byte_id = env_->GetStaticp_method(cls, "byte_method", "B:B");
    ASSERT_NE(byte_id, nullptr);
    ets_method char_id = env_->GetStaticp_method(cls, "char_method", "C:C");
    ASSERT_NE(char_id, nullptr);
    ets_method short_id = env_->GetStaticp_method(cls, "short_method", "S:S");
    ASSERT_NE(short_id, nullptr);
    ets_method int_id = env_->GetStaticp_method(cls, "int_method", ":I");
    ASSERT_NE(int_id, nullptr);
    ets_method long_id = env_->GetStaticp_method(cls, "long_method", "J:J");
    ASSERT_NE(long_id, nullptr);
    ets_method float_id = env_->GetStaticp_method(cls, "float_method", "F:F");
    ASSERT_NE(float_id, nullptr);
    ets_method double_id = env_->GetStaticp_method(cls, "double_method", "D:D");
    ASSERT_NE(double_id, nullptr);

    ets_method void_id_no_sig = env_->GetStaticp_method(cls, "void_method", nullptr);
    ASSERT_EQ(void_id, void_id_no_sig);
    ets_method object_id_no_sig = env_->GetStaticp_method(cls, "object_method", nullptr);
    ASSERT_EQ(object_id, object_id_no_sig);
    ets_method boolean_id_no_sig = env_->GetStaticp_method(cls, "boolean_method", nullptr);
    ASSERT_EQ(boolean_id, boolean_id_no_sig);
    ets_method byte_id_no_sig = env_->GetStaticp_method(cls, "byte_method", nullptr);
    ASSERT_EQ(byte_id, byte_id_no_sig);
    ets_method char_id_no_sig = env_->GetStaticp_method(cls, "char_method", nullptr);
    ASSERT_EQ(char_id, char_id_no_sig);
    ets_method short_id_no_sig = env_->GetStaticp_method(cls, "short_method", nullptr);
    ASSERT_EQ(short_id, short_id_no_sig);
    ets_method int_id_no_sig = env_->GetStaticp_method(cls, "int_method", nullptr);
    ASSERT_EQ(int_id, int_id_no_sig);
    ets_method long_id_no_sig = env_->GetStaticp_method(cls, "long_method", nullptr);
    ASSERT_EQ(long_id, long_id_no_sig);
    ets_method float_id_no_sig = env_->GetStaticp_method(cls, "float_method", nullptr);
    ASSERT_EQ(float_id, float_id_no_sig);
    ets_method double_id_no_sig = env_->GetStaticp_method(cls, "double_method", nullptr);
    ASSERT_EQ(double_id, double_id_no_sig);

    // CallStatic<type>Method part
    env_->CallStaticVoidMethod(cls, void_id, static_cast<ets_int>(42));

    ets_class a_cls = env_->FindClass("A");
    ASSERT_NE(a_cls, nullptr);
    ets_object a_obj = env_->CallStaticObjectMethod(cls, object_id);
    ASSERT_NE(a_obj, nullptr);
    EXPECT_EQ(env_->IsInstanceOf(a_obj, a_cls), ETS_TRUE);

    EXPECT_EQ(env_->CallStaticBooleanMethod(cls, boolean_id, static_cast<ets_boolean>(1)), static_cast<ets_boolean>(0));
    EXPECT_EQ(env_->CallStaticByteMethod(cls, byte_id, static_cast<ets_byte>(1)), static_cast<ets_byte>(0));
    EXPECT_EQ(env_->CallStaticCharMethod(cls, char_id, static_cast<ets_char>(1)), static_cast<ets_char>(0));
    EXPECT_EQ(env_->CallStaticShortMethod(cls, short_id, static_cast<ets_short>(1)), static_cast<ets_short>(0));
    EXPECT_EQ(env_->CallStaticIntMethod(cls, int_id), static_cast<ets_int>(0));
    EXPECT_EQ(env_->CallStaticLongMethod(cls, long_id, static_cast<ets_long>(1)), static_cast<ets_long>(0));
    EXPECT_FLOAT_EQ(env_->CallStaticFloatMethod(cls, float_id, static_cast<ets_float>(1.0F)),
                    static_cast<ets_float>(0.0F));
    EXPECT_DOUBLE_EQ(env_->CallStaticDoubleMethod(cls, double_id, static_cast<ets_double>(1.0)),
                     static_cast<ets_double>(0.0));

    // CallStatic<Type>MethodArray part
    ets_value tmp;
    tmp.i = static_cast<ets_int>(42);
    std::vector<ets_value> void_args = {tmp};
    env_->CallStaticVoidMethodArray(cls, void_id, void_args.data());

    ets_object a_obj_from_array_func = env_->CallObjectMethodArray(cls, object_id, nullptr);
    ASSERT_NE(a_obj_from_array_func, nullptr);
    EXPECT_EQ(env_->IsInstanceOf(a_obj_from_array_func, a_cls), ETS_TRUE);

    tmp.z = static_cast<ets_boolean>(1);
    std::vector<ets_value> boolean_args = {tmp};
    EXPECT_EQ(env_->CallStaticBooleanMethodArray(cls, boolean_id, boolean_args.data()), static_cast<ets_boolean>(0));

    tmp.b = static_cast<ets_byte>(1);
    std::vector<ets_value> byte_args = {tmp};
    EXPECT_EQ(env_->CallStaticByteMethodArray(cls, byte_id, byte_args.data()), static_cast<ets_byte>(0));

    tmp.c = static_cast<ets_char>(1);
    std::vector<ets_value> char_args = {tmp};
    EXPECT_EQ(env_->CallStaticCharMethodArray(cls, char_id, char_args.data()), static_cast<ets_char>(0));

    tmp.s = static_cast<ets_short>(1);
    std::vector<ets_value> short_args = {tmp};
    EXPECT_EQ(env_->CallStaticShortMethodArray(cls, short_id, short_args.data()), static_cast<ets_short>(0));

    EXPECT_EQ(env_->CallStaticIntMethodArray(cls, int_id, nullptr), static_cast<ets_int>(0));

    tmp.j = static_cast<ets_long>(1);
    std::vector<ets_value> long_args = {tmp};
    EXPECT_EQ(env_->CallStaticLongMethodArray(cls, long_id, long_args.data()), static_cast<ets_long>(0));

    tmp.j = static_cast<ets_float>(1.0F);
    std::vector<ets_value> float_args = {tmp};
    EXPECT_FLOAT_EQ(env_->CallStaticFloatMethodArray(cls, float_id, float_args.data()), static_cast<ets_float>(0.0F));

    tmp.j = static_cast<ets_double>(1.0);
    std::vector<ets_value> double_args = {tmp};
    EXPECT_DOUBLE_EQ(env_->CallStaticDoubleMethodArray(cls, double_id, double_args.data()),
                     static_cast<ets_double>(0.0));

    // CallStatic<type>MethodList part
    CallStaticVoidMethodListHelper(env_, cls, void_id, static_cast<ets_int>(42), static_cast<ets_int>(121));

    ets_object a_obj_from_list_func = CallObjectMethodListHelper(env_, cls, object_id);
    ASSERT_NE(a_obj_from_list_func, nullptr);
    EXPECT_EQ(env_->IsInstanceOf(a_obj_from_list_func, a_cls), ETS_TRUE);

    EXPECT_EQ(CallStaticBooleanMethodListHelper(env_, cls, boolean_id, static_cast<ets_boolean>(1)),
              static_cast<ets_boolean>(0));
    EXPECT_EQ(CallStaticByteMethodListHelper(env_, cls, byte_id, static_cast<ets_byte>(1)), static_cast<ets_byte>(0));
    EXPECT_EQ(CallStaticCharMethodListHelper(env_, cls, char_id, static_cast<ets_char>(1)), static_cast<ets_char>(0));
    EXPECT_EQ(CallStaticShortMethodListHelper(env_, cls, short_id, static_cast<ets_short>(1)),
              static_cast<ets_short>(0));
    EXPECT_EQ(CallStaticIntMethodListHelper(env_, cls, int_id), static_cast<ets_int>(0));
    EXPECT_EQ(CallStaticLongMethodListHelper(env_, cls, long_id, static_cast<ets_long>(1)), static_cast<ets_long>(0));
    EXPECT_FLOAT_EQ(CallStaticFloatMethodListHelper(env_, cls, float_id, static_cast<ets_float>(1.0F)),
                    static_cast<ets_float>(0.0F));
    EXPECT_DOUBLE_EQ(CallStaticDoubleMethodListHelper(env_, cls, double_id, static_cast<ets_double>(1.0)),
                     static_cast<ets_double>(0.0));

    // Check null class argument
    EXPECT_EQ(env_->CallStaticBooleanMethod(nullptr, boolean_id, static_cast<ets_boolean>(1)),
              static_cast<ets_boolean>(0));
    EXPECT_EQ(env_->CallStaticByteMethod(nullptr, byte_id, static_cast<ets_byte>(1)), static_cast<ets_byte>(0));
    EXPECT_EQ(env_->CallStaticCharMethod(nullptr, char_id, static_cast<ets_char>(1)), static_cast<ets_char>(0));
    EXPECT_EQ(env_->CallStaticShortMethod(nullptr, short_id, static_cast<ets_short>(1)), static_cast<ets_short>(0));
    EXPECT_EQ(env_->CallStaticIntMethod(nullptr, int_id), static_cast<ets_int>(0));
    EXPECT_EQ(env_->CallStaticLongMethod(nullptr, long_id, static_cast<ets_long>(1)), static_cast<ets_long>(0));
    EXPECT_FLOAT_EQ(env_->CallStaticFloatMethod(nullptr, float_id, static_cast<ets_float>(1.0F)),
                    static_cast<ets_float>(0.0F));
    EXPECT_DOUBLE_EQ(env_->CallStaticDoubleMethod(nullptr, double_id, static_cast<ets_double>(1.0)),
                     static_cast<ets_double>(0.0));

    EXPECT_EQ(env_->CallStaticBooleanMethodArray(nullptr, boolean_id, boolean_args.data()),
              static_cast<ets_boolean>(0));
    EXPECT_EQ(env_->CallStaticByteMethodArray(nullptr, byte_id, byte_args.data()), static_cast<ets_byte>(0));
    EXPECT_EQ(env_->CallStaticCharMethodArray(nullptr, char_id, char_args.data()), static_cast<ets_char>(0));
    EXPECT_EQ(env_->CallStaticShortMethodArray(nullptr, short_id, short_args.data()), static_cast<ets_short>(0));
    EXPECT_EQ(env_->CallStaticIntMethodArray(nullptr, int_id, nullptr), static_cast<ets_int>(0));
    EXPECT_EQ(env_->CallStaticLongMethodArray(nullptr, long_id, long_args.data()), static_cast<ets_long>(0));
    EXPECT_FLOAT_EQ(env_->CallStaticFloatMethodArray(nullptr, float_id, float_args.data()),
                    static_cast<ets_float>(0.0F));
    EXPECT_DOUBLE_EQ(env_->CallStaticDoubleMethodArray(nullptr, double_id, double_args.data()),
                     static_cast<ets_double>(0.0));

    EXPECT_EQ(CallStaticBooleanMethodListHelper(env_, nullptr, boolean_id, static_cast<ets_boolean>(1)),
              static_cast<ets_boolean>(0));
    EXPECT_EQ(CallStaticByteMethodListHelper(env_, nullptr, byte_id, static_cast<ets_byte>(1)),
              static_cast<ets_byte>(0));
    EXPECT_EQ(CallStaticCharMethodListHelper(env_, nullptr, char_id, static_cast<ets_char>(1)),
              static_cast<ets_char>(0));
    EXPECT_EQ(CallStaticShortMethodListHelper(env_, nullptr, short_id, static_cast<ets_short>(1)),
              static_cast<ets_short>(0));
    EXPECT_EQ(CallStaticIntMethodListHelper(env_, nullptr, int_id), static_cast<ets_int>(0));
    EXPECT_EQ(CallStaticLongMethodListHelper(env_, nullptr, long_id, static_cast<ets_long>(1)),
              static_cast<ets_long>(0));
    EXPECT_FLOAT_EQ(CallStaticFloatMethodListHelper(env_, nullptr, float_id, static_cast<ets_float>(1.0F)),
                    static_cast<ets_float>(0.0F));
    EXPECT_DOUBLE_EQ(CallStaticDoubleMethodListHelper(env_, nullptr, double_id, static_cast<ets_double>(1.0)),
                     static_cast<ets_double>(0.0));
}

}  // namespace panda::ets::test

// NOLINTEND(cppcoreguidelines-pro-type-vararg, readability-magic-numbers)